#include <stdlib.h>
#include <string.h>
#include "../include/naryTree.h"
#include "../include/header.h"
#include "../include/constants.h"
#include "../include/errors.h"
#include "../include/general_vfs_op.h"


int init_root(n_tree *root,fdesc *fd)
{
    root->data=fd;
    root->child = NULL;
    root->siblings = NULL;
    return SUCCESSFUL;
}



n_tree* allocate_node(fdesc *fd)
{
    n_tree* temp = (n_tree*) malloc(sizeof(n_tree));
    temp->data=fd;
    return temp;
}


int node_searchInSibling(n_tree *root, char* data,
                         n_tree** resultnode,n_op_type optype)
{
    if (root->siblings == NULL )
    {
        return UNSUCCESSFUL;
    }
    n_tree* node = root;
    while (node->siblings != NULL )
    {
        if (strcmp(node->siblings->data->f_name, data) == 0 &&
                strcmp(node->siblings->data->f_type, "DIR")==0)
        {

            if(optype==N_INSERT)
                *resultnode = node->siblings;
            else if(optype==N_DELETE)
                *resultnode=node;
            return FOUND;
        }
        node = node->siblings;
    }
    return INVALID;
}


int insert_in_subtree(n_tree** root, fdesc* data, int index)
{
    static char path[PATHSIZE][CHARSIZE];
    static int tokens = 0,res=-3;
    n_tree* resultnode;
    static n_tree *prevnode;
    char *prevpath;
    if(index==-1)
    {
        char *token;
        tokens=0;
        char temp[PATHSIZE];
        strcpy(temp,data->f_path);
        token=strtok(temp,"/");
        while(token!=NULL)
        {
            strcpy(path[tokens],token);
            token=strtok(NULL,"/");
            tokens++;
        }
        prevnode=*root;
        res = insert_in_subtree(&(*root)->child, data,
                                ++index);

        if (res == FOUND)
            return SUCCESSFUL;
        else
            return res;

    }

    if (index == tokens)
    {
        if (*root == NULL )
        {
            mainstr->vfs_read->f_usedfDes++;
            *root = allocate_node(data);
            (*root)->child = NULL;
            (*root)->siblings = NULL;
            return SUCCESSFUL;
        }
        else
        {
            if(isDuplicateFile(*root,data)==0)
            {
                mainstr->vfs_read->f_usedfDes++;
                n_tree* node = allocate_node(data);
                node->siblings = (*root)->siblings;
                (*root)->siblings = node;
                return SUCCESSFUL;
            }

            else
                return DUPLICATE;

        }
    }
    else if ((*root)!=NULL && strcmp((*root)->data->f_name, path[index]) == 0 &&
             strcmp((*root)->data->f_type, "DIR")==0)
    {
        prevnode=*root;
        res = insert_in_subtree(&(*root)->child,
                                data, ++index);
        return res;
    }
    else if((*root)==NULL && index!=tokens)
    {
        return insertDirRecursively(index,tokens,path,prevnode,0,data);
    }
    else
    {
        resultnode = (n_tree*) malloc(sizeof(n_tree));
        res = node_searchInSibling(*root, path[index],
                                   &resultnode,N_INSERT);
        if (res == FOUND)
        {
            prevnode=resultnode;
            res = insert_in_subtree(&(resultnode->child), data,
                                    ++index);
            return res;
        }
        else
        {
            res=insertDirRecursively(index,tokens,path,prevnode,1,data);
        }

        if (res == FOUND)
            return SUCCESSFUL;
        return res;

    }


}




int isDuplicateFile(n_tree* root,fdesc* data) /**local method for validating duplicate file**/
{
    n_tree* node=root;

    while(node!=NULL)
    {
        if(strcmp(node->data->f_name,data->f_name)==0)
        {
            return 1;
        }
        node=node->siblings;
    }
    return 0;
}
int insert_node(n_tree** root, fdesc* data)
{
    /**before inserting a node, needs to call bst for the path specified in fd as valid or invalid**/
    int res;
    res = insert_in_subtree(root, data, -1);
    return res;
}



void n_node_traversal(n_tree* root,n_traversal_type traversal,li_search s_type,FILE *ft)
{
    if(root!=NULL && traversal==LEVEL_ORDER)
    {
        fprintf (ft, "%s\n",root->data->f_name);
//        if(s_type==IS_DIR)
//        {
//
//            if(strcmp(root->data->f_type, "DIR")==0)
//
//        }
        n_node_traversal(root->siblings,LEVEL_ORDER,s_type,ft);
        n_node_traversal(root->child,LEVEL_ORDER,s_type,ft);

    }
    else if(root!=NULL && traversal==PRE_ORDER)
    {
        fprintf (ft, "%s\n",root->data->f_name);
//        if(s_type==IS_DIR)
//        {
//
//            if(strcmp(root->data->f_type, "DIR")==0)
//
//                //printf("%s %s\n",(root->data->f_path),(root->data->f_name));
//        }
        n_node_traversal(root->child,PRE_ORDER,s_type,ft);
        n_node_traversal(root->siblings,PRE_ORDER,s_type,ft);
    }
    else if(root!=NULL && traversal==POST_ORDER)
    {

        n_node_traversal(root->child,POST_ORDER,s_type,ft);
        n_node_traversal(root->siblings,POST_ORDER,s_type,ft);
        fprintf (ft, "%s\n",root->data->f_name);
    }

    else if(root!=NULL && traversal==IN_ORDER)
    {
        n_node_traversal(root->child,IN_ORDER,s_type,ft);
        fprintf (ft, "%s\n",root->data->f_name);
        n_node_traversal(root->siblings,IN_ORDER,s_type,ft);

    }
}



int dir_traversal(n_tree* root,int flag,char filepath[],char *opfile)
{
    static char path[PATHSIZE][CHARSIZE];
    static int tokens = 0;
    static int index=-1;
    static int ret=UNSUCCESSFUL;
    static FILE *ft;
    if(index==-1)
    {

        tokens=makeTokens(filepath,path);
        index++;
        ft = fopen (opfile, "w+" ) ;
        if(ft==NULL)
          return 4;
        ret=dir_traversal(root->child,flag,filepath,opfile);
        if(ft!=NULL)
        {
            index=-1;
            tokens=0;
            fclose(ft);
            ft=NULL;
        }
        return ret;
    }

    if (index == tokens)
    {
        if(root!=NULL)
        {

            fprintf (ft, "%s\n",root->data->f_name);
            if(strcmp(root->data->f_type, "DIR")==0)
            {
                //printf("%s %s\n",root->data->f_path,root->data->f_name);
                if(flag==1)
                    n_node_traversal(root->child,LEVEL_ORDER,IS_DIR,ft);
            }
            root=root->siblings;
            while(root!=NULL)
            {
                fprintf (ft, "%s\n",root->data->f_name);
                if(strcmp(root->data->f_type, "DIR")==0)
                {
                    //printf("%s %s\n",root->data->f_path,root->data->f_name);
                    if(flag==1)
                        n_node_traversal(root->child,LEVEL_ORDER,IS_DIR,ft);
                }
                root=root->siblings;
            }
            return listdir_SUCCESS;
        }

        else
            return listdir_SUCCESS;
    }

    else if(root!=NULL)
    {
        if (strcmp(root->data->f_name, path[index]) == 0 &&
                strcmp(root->data->f_type, "DIR")==0);
        else
        {
            root=root->siblings;
            while(root!=NULL)
            {
                if(strcmp(root->data->f_name, path[index]) == 0)
                {
                    if(strcmp(root->data->f_type, "DIR")==0)
                        break;
                    else
                        return VFS_07_FAILURE_NOT_A_DIR;
                }
                root=root->siblings;;
            }
            if(root==NULL)
            {

                return VFS_07_FAILURE_INVALID_FILE_PATH;
            }
        }
        index++;;
        ret=dir_traversal(root->child,flag,filepath,opfile);


//
//        if (ret == listdir_SUCCESS)
//            return listdir_SUCCESS;
//        else
//            return VFS_07_FAILURE_INVALID_FILE_PATH;
    }
    else
        return VFS_07_FAILURE_INVALID_FILE_PATH;

}
int makeTokens(char filepath[],char path[][CHARSIZE])
{

    int tokens = 0;
    char *token;
    char temp[PATHSIZE];
    strcpy(temp,filepath);
    token=strtok(temp,"/");
    while(token!=NULL)
    {
        strcpy(path[tokens],token);
        token=strtok(NULL,"/");
        tokens++;
    }
    return tokens;
}

int delete_from_subtree(n_tree** root, fdesc* data, int index)
{

    static char path[PATHSIZE][CHARSIZE];
    static int tokens = 0;
    static int op;
    if (index == -1)
    {
        char* token;
        tokens = 0;
        char temp[PATHSIZE];
        strcpy(temp, data->f_path);
        token = strtok(temp, "/");
        while (token != NULL )
        {
            strcpy(path[tokens], token);
            token = strtok(NULL, "/");
            tokens++;
        }

        op = delete_from_subtree(&(*root)->child, data,
                                 ++index);
        if (op == SUCCESSFUL)
            return SUCCESSFUL;
        else
            return op;
    }
    if (index == tokens)
    {       
        if (*root == NULL )
        {
            return UNSUCCESSFUL;
        }
        else
        {
            if (strcmp((*root)->data->f_name, data->f_name) == 0)
            {
                if((*root)->child!=NULL)
                    return NOT_EMPTY;
                *root = (*root)->siblings;
                return SUCCESSFUL;
            }
            else
            {
                n_tree* resultnode;
                resultnode = (n_tree*) malloc(sizeof(n_tree));

                op = node_searchInSibling(*root,data->f_name,
                                          &resultnode,N_DELETE);
                if (op == FOUND)
                {
                    if(resultnode->siblings->child!=NULL)
                        return NOT_EMPTY;
                    resultnode->siblings = resultnode->siblings->siblings;
                    return SUCCESSFUL;
                }
            }
        }
    }
    //printf("\nHere it is %s %s",(*root)->data->f_name,path[index]);
    if (strcmp((*root)->data->f_name, path[index]) == 0)
    {
        op = delete_from_subtree(&(*root)->child, data,
                                 ++index);

    }
    else
    {
        n_tree* resultnode;
        resultnode = (n_tree*) malloc(sizeof(n_tree));
        op= node_searchInSibling(*root, path[index],
                                 &resultnode,N_INSERT);
        if (op == FOUND)
        {
            op = delete_from_subtree(&(resultnode->child), data,
                                     ++index);
        }
        else
            return INVALIDPATH;
    }

    return op;
//    if (op == SUCCESSFUL)
//        return SUCCESSFUL;
//    else
//        return INVALIDPATH;
}

int delete_node(n_tree** root, fdesc* data)
{
    /**before deleting call binary search for the path specified in fd**/
    int op = delete_from_subtree(root, data, -1);
    return op;
}

/**need to ask can we move the mount point to somewhere else using movedir**/



int unmount_travesal(n_tree* root)
{
    fdesc *get_fd;
    static int i=0;
    if(root!=NULL && i<mainstr->vfs_read->max_fd)
    {
        get_fd=root->data;
        send_fd[i]=get_fd;
        i++;
        unmount_travesal(root->siblings);
        unmount_travesal(root->child);
    }
    return SUCCESSFUL;
}



int insertDirRecursively(int index,int tokens,char path[][CHARSIZE],
                         n_tree *prevnode,int is_child,fdesc *data)
{
    n_tree *temp_node;
    char *tempPath;
    tempPath=(char*)malloc(sizeof(char)*1024);

    int i=0;

    while(i!=index)
    {
        strcat(tempPath,"/");
        strcat(tempPath,path[i]);
        i++;
    }

    fdesc* fd = insertIntoFD(tempPath,path[index],"DIR");
    temp_node=allocate_node(fd);
    temp_node->child=NULL;
    temp_node->siblings=NULL;


    if(!is_child)
    {
        if(prevnode!=NULL)
        {
            prevnode->child=temp_node;
            prevnode=temp_node;
        }
    }
    else
    {
        prevnode=prevnode->child;
        while(prevnode!=NULL && prevnode->siblings!=NULL)
            prevnode=prevnode->siblings;

        prevnode->siblings=temp_node;
        //printf("\ndir rec %s parent %s",temp_node->data->f_name,prevnode->data->f_path);
        prevnode=temp_node;
    }

    index++;
    mainstr->vfs_read->f_usedfDes++;
    if(index!=tokens)
    {
        return insertDirRecursively(index,tokens,path,prevnode,0,data);
    }
    else
    {
        n_tree *new_node=allocate_node(data);
        new_node->child=NULL;
        new_node->siblings=NULL;
        prevnode->child=new_node;
        return SUCCESSFUL_RECURSIVE;
    }
}


int move_Function(n_tree* root,char *srcpath,char *destpath,char *temp_type)
{

    int chk,tokens;
    char tem_path[CHARSIZE];
    n_tree *srcnode=NULL;
    n_tree *destnode=NULL;
    static char srcPath[PATHSIZE][CHARSIZE];
    static char destPath[PATHSIZE][CHARSIZE];


    tokens=makeTokens(destpath,destPath);
   
    tokens=makeTokens(srcpath,srcPath);
    srcnode=tree_traversal(root,srcPath,temp_type,1);
    destnode=tree_traversal(root,destPath,"DIR",0);

   
    if(destnode!=NULL && destnode->child!=NULL)
    {
        destnode=destnode->child;
        while(destnode->siblings!=NULL)
        {
            destnode=destnode->siblings;
        }
        destnode->siblings=srcnode;

        strcpy(destnode->siblings->data->f_path,destnode->data->f_path);
       // unmount_travesal(root);
        chk=update_path(destnode->siblings,0);
        
        if(chk==1)
            return 10;
        return chk;

    }
    else
    {
        if(destnode!=NULL && destnode->child==NULL)
        {
            destnode->child=srcnode;
            strcpy(tem_path,destnode->data->f_path);
            if(strcmp(tem_path,"/")!=0)
                strcat(tem_path,"/");
            strcat(tem_path,destnode->data->f_name);
            strcpy(destnode->child->data->f_path,tem_path);

            chk=update_path(destnode->child,0);
 
            if(chk==1)
                return 10;
            return chk;
        }
    }

}
n_tree* tree_traversal(n_tree *root,char path[][CHARSIZE],char* temp_type,
                       int flag)
{
    int index=0,m=-1;

    char *f_type=NULL;
    int res=-1;
    n_tree *n_node,*temp;

    f_type=(char*)malloc(sizeof(char)*1024);
    strcpy(f_type,"DIR");
    n_node=root;
    while(strlen(path[index])>0)
    {
       
        if(strlen(path[index+1])<=0)
            strcpy(f_type,temp_type);

        if(root!=NULL)
        {
          
            if(root->child!=NULL && strcmp(root->child->data->f_name, path[index]) == 0
                    && strcmp(root->child->data->f_type,f_type)==0 )
            {
                if(flag==0)
                    n_node=root->child;
                if(flag==1)
                    n_node=root;
                m=0;
                root=root->child;
                index++;

            }
            else
            {
                root=root->child;
                while(root!=NULL)
                {

                    if(root->siblings!=NULL && strcmp(root->siblings->data->f_name, path[index]) == 0
                            && strcmp(root->siblings->data->f_type, f_type)==0 )
                    {
                        if(flag==0)
                            n_node=root->siblings;
                        if(flag==1)
                            n_node=root;

                        index++;
                        m=1;

                        root=root->siblings;

                        break;
                    }
                    root=root->siblings;
                }
            }
        }
        else
        {
            return NULL;
        }
    }
    if(flag==1)
    {
        if(m==0)
        {
            temp=(n_node->child);
            //temp->siblings=NULL;
            n_node->child=n_node->child->siblings;
            temp->siblings=NULL;
            //printf("src %p %p",temp,n_node->child);
        }
        else if(m==1)
        {
            temp=n_node->siblings;
            n_node->siblings=n_node->siblings->siblings;
            temp->siblings=NULL;
            //printf("src %p %p",temp,n_node->child);
        }
    }
    if(flag==0)
    {
        temp=n_node;
        // printf("dest %p",temp);
    }
    return temp;
}


int update_path(n_tree *node,int flag)
{
    char temp[CHARSIZE];
    static int res=-1;

    if(node!=NULL)
    {
        if(flag==0)
        {
            if(node->child!=NULL)
            {
                strcpy(temp,node->data->f_path);
                strcat(temp,"/");
                strcpy(node->child->data->f_path,strcat(temp,node->data->f_name));
            }
            node=node->child;

        }

        else if(flag==1)
        {
            if(node->siblings!=NULL)
            {
                strcpy(node->siblings->data->f_path,node->data->f_path);
            }
            node=node->siblings;
        }
        update_path(node,0);
        update_path(node,1);

    }
    return SUCCESSFUL;
}

/*int if_not_recursive(char *path,n_tree *root,int *index){
   static char dirPath[PATHSIZE][CHARSIZE];
    tokens=makeTokens(path,destPath);

}*/
int dir_tree_traversal(n_tree* root,char filepath[],char *opfile)
{
    static char path[PATHSIZE][CHARSIZE];
    static int tokens = 0;
    static int index=-1;
    static int ret=UNSUCCESSFUL;
    static FILE *ft;
    static n_tree* prev;

    if(index==-1)
    {
        prev=root;
        tokens=makeTokens(filepath,path);
        index++;
        ft = fopen (opfile, "w+" ) ;
        if(ft==NULL)
          return 0;
        ret=dir_tree_traversal(root->child,filepath,opfile);
        if(ft!=NULL)
        {
            index=-1;
            tokens=0;
            fclose(ft);
            ft=NULL;
        }
        return ret;
    }

    if (index == tokens)
    {
       
       return print_dir_struct(prev,"DIR",-1,ft);
    }

    else if(root!=NULL)
    {
        if (strcmp(root->data->f_name, path[index]) == 0 &&
                strcmp(root->data->f_type, "DIR")==0);
        else
        {
            root=root->siblings;
            while(root!=NULL)
            {
                if(strcmp(root->data->f_name, path[index]) == 0)
                {
                    if(strcmp(root->data->f_type, "DIR")==0)
                        break;
                    else
                        return VFS_07_FAILURE_NOT_A_DIR;
                }
                root=root->siblings;;
            }
            if(root==NULL)
            {

                return VFS_07_FAILURE_INVALID_FILE_PATH;
            }
        }
        index++;
        prev=root;
        ret=dir_tree_traversal(root->child,filepath,opfile);
    }
    else
        return VFS_07_FAILURE_INVALID_FILE_PATH;

}



int print_dir_struct(n_tree *node,char *temp_type,int index,FILE *ft)
{
static int chk=-1;
static int j;
if(chk==-1){
   chk++;
   if(node!=NULL){
    print_dir_struct(node->child,temp_type,index,ft);    
   }
}
else{
 if(node!=NULL && strcmp(node->data->f_type,temp_type)==0)
  {
    index++;
   for(j=0;j<=index;j++){
        fprintf (ft, "|");
     if(j<=(index-1))
        fprintf(ft,"\t");
     }
    fprintf(ft,"-- ");
    fprintf(ft,"%s",node->data->f_name);
   fprintf(ft,"\n");
  
   print_dir_struct(node->child,temp_type,index,ft);
   print_dir_struct(node->siblings,temp_type,index-1,ft);
 }
}
return 0;

}

